package com.iflytek.iot.redis.sharding.tools.config;


import com.iflytek.iot.redis.sharding.core.IRedisShardingManager;
import com.iflytek.iot.redis.sharding.core.RedisGroup;
import com.iflytek.iot.redis.sharding.core.RedisShardingManager;
import com.iflytek.iot.redis.sharding.core.entity.SlotConfig;
import com.iflytek.iot.redis.sharding.tools.constants.RedisModel;
import com.iflytek.iot.redis.sharding.tools.utils.RedisClientInstanceUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.MapPropertySource;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * @author zurun
 * @date 2020/3/3 11:15:07
 */
@Slf4j
@Component
public class ShardingRegister {
    public static final String beanName = "shardingRegister";
    @Autowired
    ShardingProperties shardingProperties;
    @Value("${iflytek.redis.sharding.jedis:false}")
    private Boolean jedis;

    IRedisShardingManager<RedisTemplate> manager = RedisShardingManager.getInstance();

    public void register() {
        if (StringUtils.isEmpty(shardingProperties.getCollection())) {
            log.warn("未获取到redis分片配置,请检查配置:iflytek.redis.sharding。服务将退出");
            System.exit(1);
        }
        shardingProperties.getCollection().forEach(shardingProperty -> {

            // 每个数据源
            RedisModel model = shardingProperty.getModel();
            shardingProperty.getDataSources().forEach(dataSource -> {
                ShardingProperty.Pool pool = dataSource.getPool();
                GenericObjectPoolConfig poolConfig = null;
                if (pool != null) {
                    // 连接池
                    poolConfig = new GenericObjectPoolConfig();
                    if (pool.getMaxIdle() != null) {
                        poolConfig.setMaxIdle(pool.getMaxIdle());
                    }
                    if (pool.getMinIdle() != null) {
                        poolConfig.setMinIdle(pool.getMinIdle());
                    }
                    if (pool.getMaxTotal() != null) {
                        poolConfig.setMaxTotal(pool.getMaxTotal());
                    }
                    if (pool.getMaxWaitMillis() != null) {
                        poolConfig.setMaxWaitMillis(pool.getMaxWaitMillis());
                    }
//                    BeanUtils.copyProperties(pool, poolConfig);
                }
                if (RedisModel.STANDALONE.equals(model)) {
                    // 单机版本redis
                    String[] node = dataSource.getNodes().split(":");
                    RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(node[0], Integer.parseInt(node[1]));
                    if (!StringUtils.isEmpty(dataSource.getPassword())) {
                        configuration.setPassword(RedisPassword.of(dataSource.getPassword()));
                    }

                    RedisTemplate redisTemplate = RedisClientInstanceUtils.createRedisTemplate(jedis, configuration, poolConfig);
                    register(redisTemplate, shardingProperty, dataSource);
                } else if (RedisModel.SENTINEL.equals(model)) {
                    // 哨兵模式
                    RedisSentinelConfiguration sentinelConfiguration = new RedisSentinelConfiguration();

                    String[] nodes = dataSource.getNodes().split(",");
                    for (int i = 0; i < nodes.length; i++) {
                        String[] hosts = nodes[i].split(":");
                        sentinelConfiguration.sentinel(hosts[0], Integer.valueOf(hosts[1]));
                    }
                    sentinelConfiguration.setMaster(dataSource.getMaster());
                    if (!StringUtils.isEmpty(dataSource.getPassword())) {
                        sentinelConfiguration.setPassword(RedisPassword.of(dataSource.getPassword()));
                    }

                    RedisTemplate redisTemplate = RedisClientInstanceUtils.createRedisTemplate(jedis, sentinelConfiguration, poolConfig);
                    register(redisTemplate, shardingProperty, dataSource);
                } else if (RedisModel.CLUSTER.equals(model)) {
                    // REDIS集群
                    Map<String, Object> source = new HashMap<>(8);
                    source.put("spring.redis.cluster.nodes", dataSource.getNodes());
                    RedisClusterConfiguration configuration = new RedisClusterConfiguration(new MapPropertySource("RedisClusterConfiguration", source));
                    if (!StringUtils.isEmpty(dataSource.getPassword())) {
                        configuration.setPassword(RedisPassword.of(dataSource.getPassword()));
                    }

                    RedisTemplate redisTemplate = RedisClientInstanceUtils.createRedisTemplate(jedis, configuration, poolConfig);
                    register(redisTemplate, shardingProperty, dataSource);
                } else {
                    throw new RuntimeException("[未知的redis类型] " + model);
                }
            });
        });

    }

    private void register(RedisTemplate t, ShardingProperty shardingProperty, ShardingProperty.DataSource dataSource) {
        SlotConfig slotConfig = new SlotConfig(dataSource.getSlotNo(), dataSource.getWeight())
                .setModel(shardingProperty.getModel().name())
                .setNodes(dataSource.getNodes())
                .setPwd(dataSource.getPassword());
        if (StringUtils.isEmpty(shardingProperty.getName())) {

            manager.register(t, slotConfig);
        } else {
            manager.register(new RedisGroup(shardingProperty.getName()), t, slotConfig);
        }
    }

}
